/**
 *
 */
package com.kjcxlab.avcviewer.video;

/**
 * @author efhkklm
 */

import com.kjcxlab.avcviewer.util.Constants;

/**
 * RtpPacket implements a RTP packet.
 */
public class H264RtpPacket {

    private int packetType = 0;
    public final static int ERROR_INDEX = 3;
    public final static byte FIRST_FU_A_PACKAGE = 1;
    public final static byte MIDLLE_FU_A_PACKAGE = 0;
    public final static byte LAST_FU_A_PACKAGE = 2;
    /* RTP packet buffer containing both the RTP header and payload */
    byte[] packet;

    /* RTP packet length */
    int packet_len;

    /**
     * Creates a new RTP packet
     */
    public final boolean setPacket(byte[] buffer) {
        packet = buffer;
        packet_len = buffer.length;

        if (packet_len <= 12) {
            System.out.println("packet is too short:" + packet_len);
            return false;
        }
        packetType = packetParser();

        return true;
    }

    /**
     * Gets the RTP packet length
     */
    public final int getLength() {
        return packet_len;
    }

    /**
     * Get package type single NAL or FU-A package
     */
    private final int packetParser() {
        // Get single nal;
        int type = 0;
        if ((packet[Constants.RTP_HEADER_LENGTH] & 0xFF & 0x1F) <= Constants.RTP_PACKAGE_SINGEL) {
            type = Constants.RTP_PACKAGE_SINGEL;
        }
        // Get FU-A
        else if ((packet[Constants.RTP_HEADER_LENGTH] & 0xFF & 0x1F) == Constants.RTP_PACKAGE_FU_A) {
            type = Constants.RTP_PACKAGE_FU_A;
        }
        // Not a standard package
        return type;
    }

    /**
     * Get package type single NAL or FU-A package
     */
    public final int getPackageType() {
        return packetType;
    }

    /** Single NAL package */
    // +------+---------------+------+
    // | 12---|0|1|2|3|4|5|6|7|-pay--|
    // |byte--+-+-+-+-+-+-+-+-+-load-|
    // |header|F|NRI|--Type---|-area-|
    // +------+---------------+------+
    /** FU-A NAL package */
    // +------+---------------+---------------+------+
    // | 12---|0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7| pay--|
    // |byte--+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ load-|
    // |header|S|E|R| Type----|F|NRI|-Type----| area-|
    // +------+---------------+---------------+------+

    /**
     * Gets the RTP payload length
     */
    public final int getPayloadLength() {
        int paylen = 0;
        if (packet_len >= 12) {
            if (getPackageType() == Constants.RTP_PACKAGE_SINGEL)
                paylen = packet_len - Constants.RTP_PACKAGE_SINGEL;
            else if (getPackageType() == Constants.RTP_PACKAGE_FU_A)
                paylen = packet_len - Constants.RTP_FU_A_HEADER_LENGTH;
        } else
            paylen = 0;// broken packet
        return paylen;
    }

    // version (V): 2 bits
    // padding (P): 1 bit
    // extension (X): 1 bit
    // CSRC count (CC): 4 bits
    // marker (M): 1 bit
    // payload type (PT): 7 bits
    // sequence number: 16 bits
    // timestamp: 32 bits
    // SSRC: 32 bits
    // CSRC list: 0 to 15 items, 32 bits each

    /**
     * Gets the version (V)
     */
    public final int getVersion() {
        if (packet_len >= 12)
            return (packet[0] >> 6 & 0x03);
        else
            return 0; // broken packet
    }

    /**
     * Whether has padding (P)
     */
    public final boolean hasPadding() {
        if (packet_len >= 12)
            return Constants.getBit(packet[0], 5);
        else
            return false; // broken packet
    }

    /**
     * Whether has extension (X)
     */
    public final boolean hasExtension() {
        if (packet_len >= 12)
            return Constants.getBit(packet[0], 4);
        else
            return false; // broken packet
    }

    /**
     * Gets the CSCR count (CC)
     */
    public final int getCscrCount() {
        if (packet_len >= 12)
            return (packet[0] & 0x0F);
        else
            return 0; // broken packet
    }

    public final boolean isIframe() {
        return (((int) packet[Constants.RTP_FU_A_HEADER_LENGTH] & 0xFF & 0x1F) == 5);
    }

    /**
     * Get |E|S| bit
     */
    public final byte getFuIndex() {
        boolean mark = ((((int) packet[1] & 0xFF) >>> 7) == 1);
        boolean sBit = ((((int) packet[Constants.RTP_FU_A_HEADER_LENGTH] & 0xFF) >>> 7) == 1);
        boolean eBit = ((((int) packet[Constants.RTP_FU_A_HEADER_LENGTH] & 0xFF & 0x40) >>> 6) == 1);

        byte index = 0;

        if (!mark && sBit && !eBit) {
            index = FIRST_FU_A_PACKAGE;
        } else if (!mark && !sBit && !eBit) {
            index = MIDLLE_FU_A_PACKAGE;
        } else if (mark && !sBit && eBit) {
            index = LAST_FU_A_PACKAGE;
        } else {
            index = ERROR_INDEX;
        }

        return index;
    }

    /**
     * Gets the payload type (PT)
     */
    public final int getPayloadType() {
        int paytype = 0;
        if (packet_len >= 12)
            paytype = (packet[1] & 0x7F);
        else
            paytype = -1; // broken packet
        return paytype;
    }

    /**
     * Gets the sequence number
     */
    public final int getSequenceNumber() {
        int seq = 0;
        if (packet_len >= 12)
            seq = Constants.getInt(packet, 2, 4);
        else
            seq = 0; // broken packet
        return seq;
    }

    /**
     * Gets the timestamp
     */
    public final long getTimestamp() {
        if (packet_len >= 12)
            return Constants.getLong(packet, 4, 8);
        else
            return 0; // broken packet
    }

    /**
     * Gets the SSCR
     */
    public final long getSscr() {
        if (packet_len >= 12)
            return Constants.getLong(packet, 8, 12);
        else
            return 0; // broken packet
    }

    /**
     * Gets the CSCR list
     */
    public final long[] getCscrList() {
        int cc = getCscrCount();
        long[] cscr = new long[cc];
        for (int i = 0; i < cc; i++)
            cscr[i] = Constants.getLong(packet, 12 + 4 * i, 16 + 4 * i);
        return cscr;
    }

    /**
     * Gets payload
     */
    public final byte[] getPayload() {

        int len = 0;
        len = getPayloadLength();
        byte[] payload = new byte[len];
        if (getPackageType() == Constants.RTP_PACKAGE_SINGEL) {
            System.arraycopy(packet, Constants.RTP_HEADER_LENGTH, payload, 0, len);
        } else if (getPackageType() == Constants.RTP_PACKAGE_FU_A) {
            payload[0] = (byte) ((packet[Constants.RTP_HEADER_LENGTH] & 0xFF & 0xE0)
                    | (packet[Constants.RTP_FU_A_HEADER_LENGTH] & 0xFF & 0x1F));
            System.arraycopy(packet, Constants.RTP_FU_A_HEADER_LENGTH + 1, payload, 1, len - 1);
        } else {
            return null;
        }
        return payload;
    }

}
